/*
This file is part of buco.  
Copyright (c) 2012 Christopher Pawelzik <christopher@cpawelzik.com>

Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php 
*/ 
package buco.jsonrpc;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpSession;

import org.apache.commons.codec.binary.Base64;
import org.json.simple.JSONObject;
import org.json.simple.JSONStreamAware;

import buco.core.api.AddBundleCommand;
import buco.core.api.AddBundleParameters;
import buco.core.api.Bundle;
import buco.core.api.BundleWire;
import buco.core.api.CompositionContext;
import buco.core.api.DuplicateBundleException;
import buco.core.api.ImportCompositionCommand;
import buco.core.api.RemoveBundleCommand;
import buco.core.api.SemanticMessage;
import buco.core.api.ToggleWireCommand;
import buco.core.api.ToggleWireParameters;
import buco.core.api.ToggleWireResult;
import buco.http.JsonRpcServlet;

@SuppressWarnings("unchecked")
public class JSONRpcAPI {

	CompositionContext context; 
	
	public JSONRpcAPI(CompositionContext context) {
		this.context = context;		
	}

	public JSONStreamAware getComposition() {
		return new BundleCompositionConverter(context.getComposition());		
	}
	
	public JSONStreamAware addBundle(JSONObject file) throws Exception {		
		String name = file.get("name").toString();		
		String content = file.get("content").toString();		
		File tempFile = getFile(name, content); 
		
		// Add the bundle to composition 
		List<SemanticMessage> warnings = null; 
		
		try {
			AddBundleCommand command = new AddBundleCommand(); 
			AddBundleParameters params = new AddBundleParameters(name, tempFile);  
			warnings = context.execute(command, params);
		} catch (DuplicateBundleException e) {
			e.printStackTrace();
			tempFile.delete();
			throw e;
		}
		// Delete temp file
		tempFile.delete();
				
		// Return semantic messages
		return new SemanticMessageConverter(warnings);
	}
	
	public JSONStreamAware importComposition(JSONObject file) throws Exception {
		String name = file.get("name").toString();		
		String content = file.get("content").toString();		
		File tempFile = getFile(name, content);
		
		context.newComposition(); 
		
		ImportCompositionCommand command = new ImportCompositionCommand();
		context.execute(command, tempFile); 

		tempFile.delete(); 		
		
		return getComposition(); 
	}
	
	private File getFile(String name, String content) {
		// Extract base64 data
		content = content.replaceAll("data:.*base64,", "");					
		byte[] data = Base64.decodeBase64(content.getBytes());		
		// Write to temp file
		File tempFile = null; 
		try {
			tempFile = File.createTempFile(name, ".jar");
			FileOutputStream fos = new FileOutputStream(tempFile); 
			fos.write(data); 
			fos.close(); 
			return tempFile;
		} catch (IOException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
			return null;
		} 
	}
	
	public JSONStreamAware removeBundle(JSONObject params) throws Exception {
		int bundleID = readInt32(params, "bundleID");
		Bundle bundle = context.getComposition().getBundleByID(bundleID);
		RemoveBundleCommand command = new RemoveBundleCommand(); 
		List<SemanticMessage> messages = context.execute(command, bundle);		
		return new SemanticMessageConverter(messages); 
	}
	
	public JSONStreamAware setBundlePosition(JSONObject params) {
		try { 
			int bundleID = readInt32(params, "bundleID");  				
			int top = readInt32(params, "top");
			int left = readInt32(params, "left");		
			Bundle bundle = context.getComposition().getBundleByID(bundleID);
			if(bundle != null) {
				bundle.setCustomProperty("top", top); 					
				bundle.setCustomProperty("left", left);
			}
		}
		catch (java.lang.Exception e) {
			e.printStackTrace();
		}
		return null; 
	}
	
	@SuppressWarnings("rawtypes")
	private int readInt32(Map map, String parameter) {
		return ((java.lang.Number)map.get(parameter)).intValue();
	}

	public JSONStreamAware checkIntegrity() {
		List<SemanticMessage> msgs = this.context.getComposition().checkIntegrity();
		return new SemanticMessageConverter(msgs); 		
	}

	public JSONStreamAware newComposition() {
		this.context.newComposition();
		return null;
	}
	
	public JSONStreamAware toggleWire(JSONObject wireInfo) throws Exception { 
		int from = readInt32(wireInfo, "from");
		int to = readInt32(wireInfo, "to");
		JSONObject result = new JSONObject(); 
		
		for(BundleWire wire : context.getComposition().getWires()) {
			if(wire.getTo().getBundleID() == to && 
			   wire.getFrom().getBundleID() == from) { 			   			
				
				ToggleWireCommand command = new ToggleWireCommand();
				ToggleWireParameters params = new ToggleWireParameters(wire); 
				ToggleWireResult wireResult = context.execute(command, params);				
				
				result.put("to", to);
				result.put("from", from);
				result.put("isActive", wire.isWireActive());
				result.put("messages", new SemanticMessageConverter(wireResult.getMessages()));
				break;
			}
		}
		
		return result;
	}
	
	public JSONStreamAware undo() {
		if(context.canUndo()) { 
			context.undo();
			return new BundleCompositionConverter(context.getComposition()); 
		}
		return null;
	}
	
	public JSONStreamAware redo() {
		if(context.canRedo()) { 
			context.redo();
			return new BundleCompositionConverter(context.getComposition()); 
		}
		return null;
	}

}
